﻿// <copyright file="BitmapBuffer.cs" company="BerlinSoft">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Wernfried Schwenkner</author>
// <email>wernfried@schwenkner.de</email>
// <date>2009-10-21</date>
// <summary>
// Contains the class of a bitmap buffer.
// </summary>

using System;
using System.Drawing;
using System.IO;

namespace TimeTracker.Controls.ColorPickerForm.Base
{
	/// <summary>
	/// Class to implements a bitmap buffer.
	/// It enables access to every byte of the bitmap by the indexer.
	/// </summary>
	public class BitmapBuffer
	{
		/// <summary>
		/// Offset to the bitmap values
		/// </summary>
		private const int Offset = 54;

		/// <summary>
		/// Number of bytes used for a pixel
		/// </summary>
		private short bytes = 3;

		/// <summary>
		/// The pixel buffer
		/// </summary>
		private byte[] buffer = null;

		/// <summary>
		/// Width of the buffer
		/// </summary>
		private int width = 0;

		/// <summary>
		/// Heught of the buffer
		/// </summary>
		private int height = 0;

		/// <summary>
		/// Byte width
		/// </summary>
		private int byteWidth = 0;

		/// <summary>
		/// Stream for writing the buffer
		/// </summary>
		private MemoryStream stream = null;

		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapBuffer"/> class.
		/// </summary>
		public BitmapBuffer()
		{
			Init(0, 0);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapBuffer"/> class.
		/// </summary>
		/// <param name="width">Width of the buffer</param>
		/// <param name="height">Height od the buffer</param>
		public BitmapBuffer(int width, int height)
		{
			Init(width, height);
		}

		/// <summary>
		/// Gets the width of the bitmap buffer.
		/// </summary>
		public int Width
		{
			get
			{
				return width;
			}
		}

		/// <summary>
		/// Gets the height of the bitmap buffer.
		/// </summary>
		public int Height
		{
			get
			{
				return height;
			}
		}

		/// <summary>
		/// Gets or sets the color value at the given buffer position
		/// </summary>
		/// <param name="x">horizontal position of the pixel</param>
		/// <param name="y">vertical position of the pixel</param>
		/// <returns>the color value at the given position</returns>
		public int this[int x, int y]
		{
			get
			{
				int index = Offset + (height - y - 1) * byteWidth + x * bytes;
				int b = buffer[index++];
				int g = buffer[index++];
				int r = buffer[index];
				return r << 16 | g << 8 | b;
			}

			set
			{
				int index = Offset + (height - y - 1) * byteWidth + x * bytes;
				buffer[index++] = (byte)(value & 0xFF);
				buffer[index++] = (byte)((value >> 8) & 0xFF);
				buffer[index] = (byte)((value >> 16) & 0xFF);
			}
		}

		/// <summary>
		/// Rezises the bitmap buffer.
		/// </summary>
		/// <param name="width">The new width</param>
		/// <param name="height">The new height</param>
		public void Resize(int width, int height)
		{
			Init(width, height);
		}

		/// <summary>
		/// Clears the bitmap buffer by setting all pixel values to a given value
		/// for each color component
		/// </summary>
		/// <param name="color">The color to be set as initial value</param>
		public void Clear(int color)
		{
			for (int i = 0; i < width * height; i++)
			{
				int index = i;
				buffer[Offset + index ++] = 0;
				buffer[Offset + index ++] = 0;
				buffer[Offset + index ++] = 0;
			}
		}

		/// <summary>
		/// Clears the bitmap buffer by setting all pixel values to black
		/// </summary>
		public void Clear()
		{
			Clear(0);
		}

		/// <summary>
		/// Create a Bitmap from the buffer
		/// </summary>
		/// <returns>The bitmap created</returns>
		public Bitmap CreateBitmap()
		{
			return new Bitmap(stream);
		}

		/// <summary>
		/// Initializes the buffer
		/// </summary>
		/// <param name="width">width of the buffer</param>
		/// <param name="height">height of the buffer</param>
		private void Init(int width, int height)
		{
			this.width = width;
			this.height = height;

			uint structureSize = 40;						// Size of this structure
			int bitmapWidth = width;						// Width of bitmap (pixels)
			int bitmapHeight = height;					// Height of bitmap (pixels)
			short bitmapPlanes = 1;							// Number of color planes
			short bitmapBitCount = 24;					// Pixel bit depth
			uint bitmapCompression = 0;					// Compression type
			uint bitmapSizeImage = 0;						// Size of uncompressed image
			int bitmapXPelsPerMeter = 0;				// Horizontal pixels per meter
			int bitmapYPelsPerMeter = 0;				// Vertical pixels per meter
			uint bitmapClrUsed = 0;							// Number of colors used
			uint bitmapClrImportant = 0;				// Important colors
			ushort reserved = 0;

			byteWidth = width * bytes;
			int r = byteWidth % 4;
			if (r != 0)
			{
				byteWidth += 4 - r;
			}

			uint buffersize = (uint)(Offset + byteWidth * height);
			buffer = new byte[buffersize];
			stream = new MemoryStream(buffer, true);
			BinaryWriter writer = new BinaryWriter(stream);

			writer.Write('B');
			writer.Write('M');
			writer.Write(buffersize);
			writer.Write(reserved);
			writer.Write(reserved);
			writer.Write(Offset);

			writer.Write(structureSize);
			writer.Write(bitmapWidth);
			writer.Write(bitmapHeight);
			writer.Write(bitmapPlanes);
			writer.Write(bitmapBitCount);
			writer.Write(bitmapCompression);
			writer.Write(bitmapSizeImage);
			writer.Write(bitmapXPelsPerMeter);
			writer.Write(bitmapYPelsPerMeter);
			writer.Write(bitmapClrUsed);
			writer.Write(bitmapClrImportant);
		}
	}
}
